﻿using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Security;
using Nop.Core.Domain.Shipping;
using Nop.Services.Catalog;
using Nop.Services.Events;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Security;
using Nop.Services.Seo;
using Nop.Services.Shipping;
using Nop.Services.Stores;
using Nop.Web.Areas.App.Models;
using Nop.Web.Factories;
using Nop.Web.Framework;
using Nop.Web.Infrastructure.Cache;
using Nop.Web.Models.Catalog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Nop.Web.Areas.App.Controllers
{
    public class ProductController : BaseAppController
    {
        #region Fields
        private readonly IStaticCacheManager _cacheManager;
        private readonly CaptchaSettings _captchaSettings;
        private readonly CatalogSettings _catalogSettings;
        private readonly IAclService _aclService;
        private readonly ICompareProductsService _compareProductsService;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly IEventPublisher _eventPublisher;
        private readonly ILocalizationService _localizationService;
        private readonly IOrderService _orderService;
        private readonly IPermissionService _permissionService;
        private readonly IProductModelFactory _productModelFactory;
        private readonly IProductService _productService;
        private readonly IRecentlyViewedProductsService _recentlyViewedProductsService;
        private readonly IStoreContext _storeContext;
        private readonly IStoreMappingService _storeMappingService;
        private readonly IUrlRecordService _urlRecordService;
        private readonly IWebHelper _webHelper;
        private readonly IWorkContext _workContext;
        private readonly IWorkflowMessageService _workflowMessageService;
        private readonly LocalizationSettings _localizationSettings;
        private readonly ShoppingCartSettings _shoppingCartSettings;
        private readonly IShippingService _shippingService;
        private readonly IProductAttributeService _productAttributeService;
        private readonly IProductAttributeParser _productAttributeParser;
        private readonly IOrderReportService _orderReportService;
        #endregion

        #region Ctor

        public ProductController(CaptchaSettings captchaSettings,
            CatalogSettings catalogSettings,
            IAclService aclService,
            ICompareProductsService compareProductsService,
            ICustomerActivityService customerActivityService,
            IEventPublisher eventPublisher,
            ILocalizationService localizationService,
            IOrderService orderService,
            IPermissionService permissionService,
            IProductModelFactory productModelFactory,
            IProductService productService,
            IRecentlyViewedProductsService recentlyViewedProductsService,
            IStoreContext storeContext,
            IStoreMappingService storeMappingService,
            IUrlRecordService urlRecordService,
            IWebHelper webHelper,
            IWorkContext workContext,
            IWorkflowMessageService workflowMessageService,
            LocalizationSettings localizationSettings,
            ShoppingCartSettings shoppingCartSettings,
            IShippingService shippingService,
            IStaticCacheManager cacheManager,
            IProductAttributeService productAttributeService,
            IProductAttributeParser productAttributeParser,
            IOrderReportService orderReportService)
        {
            this._captchaSettings = captchaSettings;
            this._catalogSettings = catalogSettings;
            this._aclService = aclService;
            this._compareProductsService = compareProductsService;
            this._customerActivityService = customerActivityService;
            this._eventPublisher = eventPublisher;
            this._localizationService = localizationService;
            this._orderService = orderService;
            this._permissionService = permissionService;
            this._productModelFactory = productModelFactory;
            this._productService = productService;
            this._recentlyViewedProductsService = recentlyViewedProductsService;
            this._storeContext = storeContext;
            this._storeMappingService = storeMappingService;
            this._urlRecordService = urlRecordService;
            this._webHelper = webHelper;
            this._workContext = workContext;
            this._workflowMessageService = workflowMessageService;
            this._localizationSettings = localizationSettings;
            this._shoppingCartSettings = shoppingCartSettings;
            this._cacheManager = cacheManager;
            this._shippingService = shippingService;
            this._productAttributeService = productAttributeService;
            this._productAttributeParser = productAttributeParser;
            this._orderReportService = orderReportService;
        }

        #endregion


        public virtual JsonResult ProductDetails(int productId, bool appMode = true)
        {
            var product = _productService.GetProductById(productId);
            if (product == null || product.Deleted)
                return InvokeHttp404();

            var notAvailable =
                //published?
                (!product.Published && !_catalogSettings.AllowViewUnpublishedProductPage) ||
                //ACL (access control list) 
                !_aclService.Authorize(product) ||
                //Store mapping
                !_storeMappingService.Authorize(product) ||
                //availability dates
                !_productService.ProductIsAvailable(product);
            //Check whether the current user has a "Manage products" permission (usually a store owner)
            //We should allows him (her) to use "Preview" functionality
            if (notAvailable && !_permissionService.Authorize(StandardPermissionProvider.ManageProducts))
                return InvokeHttp404();

            //visible individually?
            if (!product.VisibleIndividually)
            {
                //is this one an associated products?
                return Json(new AppResult
                {
                    Code = 500,
                    Message = "组商品不能单独访问"
                });
            }

            //save as recently viewed
            _recentlyViewedProductsService.AddProductToRecentlyViewedList(product.Id);

            //activity log
            _customerActivityService.InsertActivity("PublicStore.ViewProduct",
                string.Format(_localizationService.GetResource("ActivityLog.PublicStore.ViewProduct"), product.Name), product);

            //model
            ProductDetailsModel productModel = _productModelFactory.PrepareProductDetailsModel(product, appMode: appMode);


            //============================  relatedProducts ==================================


            IEnumerable<ProductOverviewModel> relatedProducts = new List<ProductOverviewModel>();

            //load and cache report
            var relatedProductIds = _cacheManager.Get(string.Format(ModelCacheEventConsumer.PRODUCTS_RELATED_IDS_KEY, productId, _storeContext.CurrentStore.Id),
                () => _productService.GetRelatedProductsByProductId1(productId).Select(x => x.ProductId2).ToArray());

            //load products
            var relatedProductList = _productService.GetProductsByIds(relatedProductIds);
            //ACL and store mapping
            relatedProductList = relatedProductList.Where(p => _aclService.Authorize(p) && _storeMappingService.Authorize(p)).ToList();
            //availability dates
            relatedProductList = relatedProductList.Where(p => _productService.ProductIsAvailable(p)).ToList();
            //visible individually
            relatedProductList = relatedProductList.Where(p => p.VisibleIndividually).ToList();

            if (relatedProductList.Any())
            {
                relatedProducts = _productModelFactory.PrepareProductOverviewModels(relatedProductList, true, true).ToList();
            }

            //=============================    alsoPurchasedProducts         =================================
            IEnumerable<ProductOverviewModel> alsoPurchasedProducts = new List<ProductOverviewModel>();


            //load and cache report
            var alsoPurchasedProductIds = _cacheManager.Get(string.Format(ModelCacheEventConsumer.PRODUCTS_ALSO_PURCHASED_IDS_KEY, productId, _storeContext.CurrentStore.Id),
                () => _orderReportService.GetAlsoPurchasedProductsIds(_storeContext.CurrentStore.Id, productId, _catalogSettings.ProductsAlsoPurchasedNumber)
            );

            //load products
            var alsoPurchasedProductList = _productService.GetProductsByIds(alsoPurchasedProductIds);
            //ACL and store mapping
            alsoPurchasedProductList = alsoPurchasedProductList.Where(p => _aclService.Authorize(p) && _storeMappingService.Authorize(p)).ToList();
            //availability dates
            alsoPurchasedProductList = alsoPurchasedProductList.Where(p => _productService.ProductIsAvailable(p)).ToList();

            if (relatedProductList.Any())
            {
                alsoPurchasedProducts = _productModelFactory.PrepareProductOverviewModels(alsoPurchasedProductList, true, true).ToList();
            }

            //=============================    Warehouse         =================================
            Warehouse warehouse = null;
            if (productModel.WarehouseIdList.Length > 0)
            {
                warehouse = _shippingService.GetWarehouseById(productModel.WarehouseIdList.Take(1).First());
            }



            //=============================    combinations         =================================
            var combinations = _productAttributeService.GetAllProductAttributeCombinations(product.Id).Select((s) =>
            {
                var values = _productAttributeParser.ParseProductAttributeValues(s.AttributesXml);
                return new { combinationId = s.Id, valuesIds = values.Select(v => v.Id).ToList(), s.OverriddenPrice, s.PictureId, s.StockQuantity };
            });


            //=============================    productAttributes         =================================
            var productAttributes = _productAttributeService.GetProductAttributeMappingsByProductId(product.Id).Select(a =>
            {
                return new
                {
                    a.DisplayOrder,
                    AttributeMapId = a.Id,
                    AttributeId = a.ProductAttribute.Id,
                    a.ProductAttribute.Name,
                    Values = a.ProductAttributeValues.Select(v => new
                    {
                        ValueId = v.Id,
                        v.PictureId,
                        v.Name,
                        v.PriceAdjustment,
                        v.Quantity
                    })
                };
            });


            //=============================    reviews         =================================
            var reviews = product.ProductReviews.Select(s => new { ReviewId = s.Id, s.HelpfulYesTotal, s.Rating, s.ReviewText, s.CustomerId, s.CreatedOnUtc, s.ReplyText });


            var tags = _productModelFactory.PrepareProductTagModels(product, false);

            return Json(new AppResult
            {
                Result = new
                {
                    Product = productModel,
                    RelatedProducts = relatedProducts,
                    AlsoPurchasedProducts = alsoPurchasedProducts,
                    Warehouse = warehouse,
                    Combinations = combinations,
                    ProductAttributes = productAttributes,
                    Reviews = reviews,
                    tags = tags
                }
            });
        }
    }
}
